Imports System.IO

''' <summary>
''' NCB612DTS 南兴六面钻数控图服务
''' </summary>
Public Class NCB612DTService

    ' 服务


    ''' <summary>
    ''' 工件数据表对象
    ''' </summary>
    ''' <returns></returns>
    Public Property panelList As New List(Of PanelEntity)



    ''' <summary>
    ''' 构建工件数据表
    ''' </summary>
    ''' <returns></returns>
    Public Function buildPanelList(ByRef entityDataTable As DataTable) As List(Of PanelEntity)
        panelList = New List(Of PanelEntity)
        ' 获取工件行
        Dim partsRow() As DataRow = entityDataTable.Select("Layer = '1_工件'")
        For Each row As DataRow In partsRow
            Dim part As PanelEntity = buildPanelEntity(row) ' 转换工件行数据

            ' 添加垂直孔
            add垂直孔(part, row, entityDataTable)
            ' 添加水平孔
            add水平孔(part, row, entityDataTable)
            ' 添加垂直槽
            add垂直槽(part, row, entityDataTable)
            ' 添加拉米诺_垂直
            add拉米诺_垂直(part, row, entityDataTable)
            ' 添加拉米诺_水平
            add拉米诺_水平(part, row, entityDataTable)

            ' 添加到工件行集合
            panelList.Add(part)
        Next

        Return panelList
    End Function


    Private Sub add垂直孔(ByRef part As PanelEntity, ByRef partRow As DataRow, ByRef cadEntityData As DataTable)

        Dim dv As New DataView(cadEntityData)
        dv.RowFilter = $"(Layer = '1_正面孔' or Layer = '1_反面孔' ) and (ObjectName = 'AcDbCircle' or ObjectName = 'AcDbArc') and PositionX >= {partRow("MinX") - 0.1} and PositionX <= {partRow("MaxX") + 0.1} and PositionY >= {partRow("MinY") - 0.1} and PositionY <= {partRow("MaxY") + 0.1}"

        For Each drv As DataRowView In dv
            Dim m As MachiningEntity = buildPanelEntityMachinig_垂直孔(drv.Row, partRow)
            part.Machines.Add(m)
        Next
    End Sub
    Private Sub add水平孔(ByRef part As PanelEntity, ByRef partRow As DataRow, ByRef cadEntityData As DataTable)
        Dim dv As New DataView(cadEntityData)
        dv.RowFilter = $"Layer = '1_侧面孔' and (ObjectName = 'AcDbPolyline') and PositionX >= {partRow("MinX") - 0.1} and PositionX <= {partRow("MaxX") + 0.1} and PositionY >= {partRow("MinY") - 0.1} and PositionY <= {partRow("MaxY") + 0.1}"

        For Each drv As DataRowView In dv
            Dim m As MachiningEntity = buildPanelEntityMachinig_水平孔(drv.Row, partRow)
            part.Machines.Add(m)
        Next
    End Sub
    Private Sub add垂直槽(ByRef part As PanelEntity, ByRef partRow As DataRow, ByRef cadEntityData As DataTable)
        Dim dv As New DataView(cadEntityData)
        dv.RowFilter = $"(Layer = '1_正面槽' or Layer = '1_反面槽') and (ObjectName = 'AcDbPolyline') and PositionX >= {partRow("MinX") - 0.1} and PositionX <= {partRow("MaxX") + 0.1} and PositionY >= {partRow("MinY") - 0.1} and PositionY <= {partRow("MaxY") + 0.1}"

        For Each drv As DataRowView In dv
            Dim m As MachiningEntity = buildPanelEntityMachinig_垂直槽(drv.Row, partRow)
            part.Machines.Add(m)
        Next
    End Sub
    Private Sub add拉米诺_垂直(ByRef part As PanelEntity, ByRef partRow As DataRow, ByRef cadEntityData As DataTable)
        Dim dv As New DataView(cadEntityData)
        dv.RowFilter = $"(Layer = '1_拉米诺') and (ObjectName = 'AcDbPolyline') and PositionX >= {partRow("MinX") - 0.1} and PositionX <= {partRow("MaxX") + 0.1} and PositionY >= {partRow("MinY") - 0.1} and PositionY <= {partRow("MaxY") + 0.1}"

        For Each drv As DataRowView In dv
            Dim m As MachiningEntity = buildPanelEntityMachinig_拉米诺_垂直(drv.Row, partRow)
            part.Machines.Add(m)
        Next
    End Sub
    Private Sub add拉米诺_水平(ByRef part As PanelEntity, ByRef partRow As DataRow, ByRef cadEntityData As DataTable)
        Dim dv As New DataView(cadEntityData)
        dv.RowFilter = $"(Layer = '1_拉米诺') and (ObjectName = 'AcDbArc') and PositionX >= {partRow("MinX") - 0.1} and PositionX <= {partRow("MaxX") + 0.1} and PositionY >= {partRow("MinY") - 0.1} and PositionY <= {partRow("MaxY") + 0.1}"

        For Each drv As DataRowView In dv
            Dim m As MachiningEntity = buildPanelEntityMachinig_拉米诺_水平(drv.Row, partRow)
            part.Machines.Add(m)
        Next
    End Sub


    ' 构建工件加工数据(NCB612DT六面钻设备)
    Private Function buildPanelEntity(ByRef row As DataRow) As PanelEntity
        Dim pe As New PanelEntity
        With pe
            .handle = row("Handle")
            .id = row("ObjectID")
            .name = row("PartName")
            .isProduce = True
            .machiningPoint = 1
            .lenght = Math.Round(row("MaxX") - row("MinX"), 6)
            .width = Math.Round(row("MaxY") - row("MinY"), 6)
            .thickness = Math.Abs(row("Thickness"))
        End With
        Return pe
    End Function

    ' 构建加工信息_垂直孔
    Private Function buildPanelEntityMachinig_垂直孔(ByRef row As DataRow, ByRef panelRow As DataRow) As MachiningEntity
        Dim m As New MachiningEntity
        With m
            .objectId = row("ObjectID")
            .handle = row("Handle")
            .type = "2" ' 水平孔为1 垂直孔为2 垂直槽4
            .isGenCode = "2"

            If row("Thickness") > 0 Then '6#面孔(既下面,Z为负)
                .face = "6"
            ElseIf row("Thickness") < 0 Then '5#面孔(既上面孔,Z为正)
                .face = "5"
            Else
                .face = "无法判定"
                Throw New ArgumentException($"{row("Handle")} 无法判定孔方向")
            End If


            .X = Math.Round(row("PositionX") - panelRow("MinX"), 6)
            .Y = Math.Round(row("PositionY") - panelRow("MinY"), 6)
            .Z = Nothing
            .diameter = Math.Round(row("Diameter"), 6)
            .depth = Math.Round(Math.Abs(row("Thickness")), 6)
        End With
        Return m
    End Function

    ' 构建加工信息_水平孔
    Private Function buildPanelEntityMachinig_水平孔(ByRef row As DataRow, ByRef panelRow As DataRow) As MachiningEntity
        Dim m As New MachiningEntity
        With m
            .objectId = row("ObjectID")
            .handle = row("Handle")
            .type = "1" ' 水平孔为1 垂直孔为2 垂直槽4
            .isGenCode = "2"

            If Math.Abs(row("StartPointX") - row("EndPointX")) < 0.1 Then 'Y方向孔, 1 2面
                If Math.Abs(row("StartPointY") - panelRow("MinY")) < 0.1 Then '孔在1面
                    .face = "2"
                ElseIf Math.Abs(row("StartPointY") - panelRow("MaxY")) < 0.1 Then '孔在4面
                    .face = "1"
                Else
                    .face = "无法判定"
                    Throw New ArgumentException("判定为1或4面孔, 但无法确认具体位于哪一个面上")
                End If
            ElseIf Math.Abs(row("StartPointY") - row("EndPointY")) < 0.1 Then 'X方向孔, 3 4面
                If Math.Abs(row("StartPointX") - panelRow("MinX")) < 0.1 Then '孔在2面
                    .face = "4"
                ElseIf Math.Abs(row("StartPointX") - panelRow("MaxX")) < 0.1 Then '孔在3面
                    .face = "3"
                Else
                    .face = "无法判定"
                    Throw New ArgumentException("判定为2或3面孔, 但无法确认具体位于哪一个面上")
                End If

            Else '非正交水平孔
                .face = "无法判定"
                Throw New ArgumentException("非正交水平孔")
            End If


            .X = Math.Round(row("StartPointX") - panelRow("MinX"), 6)
            .Y = Math.Round(row("StartPointY") - panelRow("MinY"), 6)
            .Z = Math.Round(Math.Abs(row("Elevation")), 6) '标高位置, 注意板件厚度从零点向下生长, 所以水平孔的标高位置为负值, 这里需要取绝对值
            .diameter = Math.Round(row("ConstantWidth"), 6) ' 
            .depth = Math.Round(GetDistance(row("StartPointX"), row("StartPointY"), row("EndPointX"), row("EndPointY")), 6)
        End With
        Return m

    End Function


    ' 构建加工信息_垂直槽(多段线 线宽为全局槽宽, 厚度为深度, )
    Private Function buildPanelEntityMachinig_垂直槽(ByRef row As DataRow, ByRef panelRow As DataRow) As MachiningEntity
        Dim m As New MachiningEntity
        With m
            .objectId = row("ObjectID")
            .handle = row("Handle")
            .isGenCode = "2"
            .type = "4" ' 水平孔为1 垂直孔为2 垂直槽4

            If row("Thickness") > 0 Then ' 6#面孔(既下面,Z为负)
                .face = "6"
            ElseIf row("Thickness") < 0 Then '5#面孔(既上面孔,Z为正)
                .face = "5"
            Else
                .face = "无法判定"
                Throw New ArgumentException($"{row("Handle")} 无法判定孔方向")
            End If

            .X = Math.Round(row("StartPointX") - panelRow("MinX"), 6)
            .Y = Math.Round(row("StartPointY") - panelRow("MinY"), 6)
            .Z = 0

            .EndX = Math.Round(row("EndPointX") - panelRow("MinX"), 6)
            .EndY = Math.Round(row("EndPointY") - panelRow("MinY"), 6)
            .EndZ = 0

            .width = Math.Round(row("ConstantWidth"), 6)
            .depth = Math.Abs(Math.Round(row("Thickness"), 6))
            .toolOffset = "中"

        End With

        Return m
    End Function

    ' 构建加工信息_拉米诺_垂直: 使用多段线, 全局宽度表示槽宽, 厚度表示槽深(注意上面的拉米诺槽槽深为负), 线长度为槽长, 中心位置为基准位置
    Private Function buildPanelEntityMachinig_拉米诺_垂直(ByRef row As DataRow, ByRef panelRow As DataRow) As MachiningEntity
        Dim m As New MachiningEntity
        With m
            .objectId = row("ObjectID")
            .handle = row("Handle")
            .isGenCode = "2"
            .type = "1002" ' 水平孔为1 垂直孔为2 垂直槽4 拉米诺1002

            If row("Thickness") > 0 Then ' 6#面孔(既下面,Z为负)
                .face = "6"
                .errMsg = "5#面不能加工拉米诺"
            ElseIf row("Thickness") < 0 Then '5#面孔(既上面孔,Z为正)
                .face = "5"
            Else
                .face = "-1"
                .errMsg = "5#面不能加工拉米诺"
            End If

            .length = Math.Round(row("Length"), 6)
            .width = Math.Round(row("ConstantWidth"), 6)
            .maxDepth = Math.Abs(Math.Round(row("Thickness"), 6))

            .X = (row("StartPointX") + row("EndPointX")) / 2 - panelRow("MinX")
            .Y = (row("StartPointY") + row("EndPointY")) / 2 - panelRow("MinY")
            .Z = 0

            If Math.Abs(row("StartPointX") - row("EndPointX")) < 0.1 Then
                .direction = "Y"
            ElseIf Math.Abs(row("StartPointY") - row("EndPointY")) < 0.1 Then
                .direction = "X"
            Else
                .direction = "ERROR"
                .errMsg = "方向异常,仅支持X或Y方向"
            End If

        End With

        Return m
    End Function

    ' 构建加工信息_拉米诺_水平, 使用arc, 厚度表示槽宽, 两点距离表示槽长, 拱高表示槽深
    Private Function buildPanelEntityMachinig_拉米诺_水平(ByRef row As DataRow, ByRef panelRow As DataRow) As MachiningEntity
        Dim m As New MachiningEntity
        With m
            ' 基础属性
            .objectId = row("ObjectID")
            .handle = row("Handle")
            .isGenCode = "2"
            .type = "1002" ' 水平孔为1 垂直孔为2 垂直槽4 拉米诺1002

            ' 所在加工面推算
            If Math.Abs(row("StartPointX") - row("EndPointX")) < 0.1 Then 'Y方向孔, 3 4面
                .direction = "Y" '安装方向
                If Math.Abs(row("PositionX") - panelRow("MinX")) < 0.1 Then '孔在4面
                    .face = "4"

                ElseIf Math.Abs(row("PositionX") - panelRow("MaxX")) < 0.1 Then '孔在3面
                    .face = "3"

                Else
                    .face = "ERROR"
                    .errMsg += "位置异常,无法判断所在加工面;"

                End If
            ElseIf Math.Abs(row("StartPointY") - row("EndPointY")) < 0.1 Then 'X方向孔, 1 2面
                .direction = "X" '安装方向

                If Math.Abs(row("PositionY") - panelRow("MinY")) < 0.1 Then '孔在2面
                    .errMsg += "2#面不能加工拉米诺;"
                    .face = "2"

                ElseIf Math.Abs(row("PositionY") - panelRow("MaxY")) < 0.1 Then '孔在1面
                    .face = "1"

                Else
                    .face = "ERROR"
                    .errMsg += "位置异常,无法判断所在加工面;"

                End If

            Else '非正交水平孔
                .direction = "ERROR" '安装方向
                .face = "ERROR"
                .errMsg += "方向异常,无法判断所在加工面;"
            End If

            ' 尺寸与位置推算
            Dim length As Double = Math.Abs(GetDistance(row("StartPointX"), row("StartPointY"), row("EndPointX"), row("EndPointY")))
            Dim radius As Double = row("Diameter") / 2 ' 圆弧半径, 用于推算拱高

            .length = Math.Round(length, 6)
            .width = Math.Abs(Math.Round(row("Thickness"), 6))
            .maxDepth = Math.Round(拱高(radius, length), 6)

            .X = Math.Round(row("PositionX") - panelRow("MinX"), 6)
            .Y = Math.Round(row("PositionY") - panelRow("MinY"), 6)
            .Z = Math.Abs(Math.Round(row("PositionZ") - panelRow("MaxZ") + (row("Thickness")) / 2, 6))



        End With

        Return m
    End Function


    ''' <summary>
    ''' 推算多线开槽补偿
    ''' </summary>
    ''' <param name="v"></param>
    ''' <returns></returns>
    Private Function justification_ToToolOffset(ByRef v As Integer) As String
        Dim toolOffset As String
        Select Case v
            Case 0 : toolOffset = "右"
            Case 1 : toolOffset = "中"
            Case 2 : toolOffset = "左"
            Case Else : toolOffset = "中"
        End Select
        Return toolOffset
    End Function


    ''' <summary>
    ''' 计算圆弧拱高
    ''' </summary>
    ''' <param name="radius"> 圆弧半径 </param>
    ''' <param name="length"> 圆弧玄长 </param>
    ''' <returns></returns>
    Private Function 拱高(ByVal radius As Double, ByVal length As Double) As Double
        Return radius - Math.Sqrt(radius ^ 2 - (length / 2) ^ 2)
    End Function


    ''' <summary>
    ''' 计算两坐标点距离
    ''' </summary>
    ''' <param name="sx"></param>
    ''' <param name="sy"></param>
    ''' <param name="ex"></param>
    ''' <param name="ey"></param>
    ''' <returns></returns>
    Private Function GetDistance(sx As Double, sy As Double, ex As Double, ey As Double) As Double
        Return Math.Sqrt((ex - sx) ^ 2 + (ey - sy) ^ 2)
    End Function

    ''' <summary>
    ''' 计算两坐标点距离
    ''' </summary>
    ''' <param name="sx"></param>
    ''' <param name="sy"></param>
    ''' <param name="sz"></param>
    ''' <param name="ex"></param>
    ''' <param name="ey"></param>
    ''' <param name="ez"></param>
    ''' <returns></returns>
    Private Function GetDistance(sx As Double, sy As Double, sz As Double, ex As Double, ey As Double, ez As Double) As Double
        Return Math.Sqrt((ex - sx) ^ 2 + (ey - sy) ^ 2 + (ez - sz) ^ 2)
    End Function



#Region "打开与保存"
    ''' <summary>
    ''' 保存
    ''' </summary>
    Public Sub Save(ByRef str As String)
        Dim xdoc As XDocument
        xdoc = XDocument.Parse(str)
        xdoc.Add()

        Dim fd As New SaveFileDialog
        With fd
            .Title = "保存为南兴六面钻加工文件"
            .Filter = "南兴六面钻加工文件(*.SCX)|*.SCX"
            .ShowDialog()
        End With

        Dim filename As String = fd.FileName
        If filename = "" Then Exit Sub

        xdoc.Save(filename)
    End Sub

    ''' <summary>
    ''' 保存全部
    ''' </summary>
    Public Sub SaveAll(ByRef pList As List(Of PanelEntity))
        Dim fd As New SaveFileDialog
        With fd
            .Title = "选择输出路径"
            .Filter = "南兴六面钻加工文件(*.SCX)|*.SCX"
            .FileName = "_"
        End With

        If fd.ShowDialog() = DialogResult.OK Then
            Dim fileDir As String = Path.GetDirectoryName(fd.FileName)
            For Each part As PanelEntity In pList
                Dim xd As XDocument = buildDoc(part)
                xd.Save($"{fileDir}/{part.name}_{part.handle}.SCX")
            Next
        End If
    End Sub
#End Region




#Region "文档结构, 构建文档"
#Region " PanelEntity 转 XDocument"



    ''' <summary>
    ''' 保存对象
    ''' </summary>
    ''' <param name="xdoc"></param>
    ''' <param name="path"></param>
    Public Sub save(ByRef xdoc As XDocument, ByRef path As String)
        xdoc.Save(path)
    End Sub

    ''' <summary>
    ''' 构建xml文档
    ''' </summary>
    ''' <param name="p"> PanelEntity对象 </param>
    ''' <returns></returns>
    Public Function buildDoc(ByRef p As PanelEntity) As XDocument


        '构建加工组
        Dim machines As XElement = buildMachines()
        For Each item In p.Machines
            machines.Add(buildMachining(item))
        Next

        '构建封边组
        Dim edgegroup As XElement = buildEdgeGroup()
        Dim edge As XElement
        For i = 1 To 4
            edge = biuldEdge(i)
            edgegroup.Add(edge)
        Next

        '构建outline
        Dim outline As XElement = buildOutline(p.lenght, p.width)

        '构建工件
        Dim panel As XElement = buildPanel(p)
        With panel
            .Add(outline)
            .Add(machines)
            .Add(edgegroup)
        End With

        '构建文档
        Dim doc As XDocument = buildDoc()
        doc.Element("Root").Element("Project").Element("Panels").Add(panel)

        Return doc

    End Function

    ''' <summary>
    ''' 构建文档
    ''' </summary>
    Private Function buildDoc() As XDocument
        Dim doc As XDocument = New XDocument(New XDeclaration("1.0", "utf-8", "yes"),
                                              New XElement("Root", New XAttribute("Cad", "BuiltInCad"), New XAttribute("version", "2.0"),
                                                           New XElement("Project",
                                                                        New XElement("Panels"))))
        Return doc
    End Function

    Private Function buildPanel(ByRef p As PanelEntity) As XElement
        Dim panel As XElement = New XElement("Panel")

        If Not p.isProduce Is Nothing Then panel.Add(New XAttribute("IsProduce", p.isProduce.ToString))
        If Not p.id Is Nothing Then panel.Add(New XAttribute("ID", p.handle))
        If Not p.name Is Nothing Then panel.Add(New XAttribute("Name", p.name))
        If Not p.width Is Nothing Then panel.Add(New XAttribute("Width", p.width))
        If Not p.lenght Is Nothing Then panel.Add(New XAttribute("Length", p.lenght))
        If Not p.thickness Is Nothing Then panel.Add(New XAttribute("Thickness", p.thickness))
        If Not p.machiningPoint Is Nothing Then panel.Add(New XAttribute("MachiningPoint", p.machiningPoint))

        Return panel
    End Function


    Private Function buildOutline(ByVal x As Double, ByVal y As Double) As XElement
        Dim outline As New XElement("Outline",
                                    New XElement("Point", New XAttribute("X", x), New XAttribute("Y", y)),
                                    New XElement("Point", New XAttribute("X", 0), New XAttribute("Y", y)),
                                    New XElement("Point", New XAttribute("X", 0), New XAttribute("Y", 0)),
                                    New XElement("Point", New XAttribute("X", x), New XAttribute("Y", 0))
                                    )
        Return outline
    End Function

    Private Function buildMachines() As XElement
        Dim machines As New XElement("Machines")
        Return machines
    End Function

    Private Function buildMachining(ByRef m As MachiningEntity) As XElement
        Dim machine As New XElement("Machining")

        If Not m.type Is Nothing Then machine.Add(New XAttribute("Type", m.type))
        If Not m.isGenCode Is Nothing Then machine.Add(New XAttribute("IsGenCode", m.isGenCode))
        If Not m.face Is Nothing Then machine.Add(New XAttribute("Face", m.face))
        If Not m.X Is Nothing Then machine.Add(New XAttribute("X", m.X))
        If Not m.Y Is Nothing Then machine.Add(New XAttribute("Y", m.Y))
        If Not m.Z Is Nothing Then machine.Add(New XAttribute("Z", m.Z))

        If Not m.diameter Is Nothing Then machine.Add(New XAttribute("Diameter", m.diameter))
        If Not m.depth Is Nothing Then machine.Add(New XAttribute("Depth", m.depth))
        If Not m.direction Is Nothing Then machine.Add(New XAttribute("Direction", m.direction))

        If Not m.EndX Is Nothing Then machine.Add(New XAttribute("EndX", m.EndX))
        If Not m.EndY Is Nothing Then machine.Add(New XAttribute("EndY", m.EndY))
        If Not m.EndZ Is Nothing Then machine.Add(New XAttribute("EndZ", m.EndZ))
        If Not m.width Is Nothing Then machine.Add(New XAttribute("Width", m.width))
        If Not m.toolOffset Is Nothing Then machine.Add(New XAttribute("ToolOffset", m.toolOffset))

        If Not m.maxDepth Is Nothing Then machine.Add(New XAttribute("MaxDepth", m.maxDepth))
        If Not m.length Is Nothing Then machine.Add(New XAttribute("Length", m.length))


        Return machine
    End Function


    Private Function buildEdgeGroup() As XElement
        Dim edgeGroup As New XElement("EdgeGroup")
        edgeGroup.Add(New XAttribute("X1", "0"))
        edgeGroup.Add(New XAttribute("Y1", "0"))
        Return edgeGroup
    End Function


    Private Function biuldEdge(ByVal face As Integer) As XElement
        Dim edge As New XElement("Edge",
                                 New XAttribute("Face", face),
                                 New XAttribute("Thickness", "0"),
                                 New XAttribute("Pre_Milling", "0"),
                                 New XAttribute("X", "0"),
                                 New XAttribute("Y", "0"),
                                 New XAttribute("CentralAngle", "0"))
        Return edge
    End Function


#End Region
#End Region
End Class
